#pragma once

#include <ddblk/apds/combine_int.h>
#include <dbg.h>

typedef struct _ghsynnode
{
	APDSFUTURE_DECL
	struct _ghsynnode *next;
} _ghsynnode_t;

typedef struct _ghsyn
{
	_ghsynnode_t * tail;
} _ghsyn_t;

STA_OUFUN(ghsyn_get)
_ghsyn_t *(* const get_ghsyn)(cid_t numa_id);
END_OUFUN(ghsyn_get)


/* 指定全局锁 */
/* 依赖一个锁 */

OU2INFUN(lock, ghsyn_glk)

/* 获得指定numa_id的ghsyn */
OU2INFUN(ghsyn_get, ghsyn_get)


/* 实现接口 */

SAI void __ghsyn_init(_ghsyn_t *s, _ghsynnode_t *sentry)
{
	s->tail = sentry;
	sentry->next = NULL;
	sentry->flag = COMBINE_FLG_OWNED;
}
SAI void *__ghsyn_destruct(_ghsyn_t *s, _ghsynnode_t **psentry){
	/* 什么也不做 */
	PARAM_USED(s, psentry);
	// ASSERT(s->tail);
	return (void *)s;
}
SAI _ghsynnode_t *__ghsyn_fetch(_ghsyn_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _ghsynnode_t *node){
	PARAM_USED(s);
	apds_movargs(&node->args, args);
	node->func = pfunc;


	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;
	/* cmb(); */
	return node;
}

SAI apdsret_t __ghsyn_wait(_ghsyn_t *s, _ghsynnode_t **pnode, lock_t *glock, INFUN(ghsyn_glk, lkop), INFUN(cmbn_wait, waitpolicy)){
	_ghsynnode_t *next_node, *cur_node, *tmp_node, *tmp_node_next;
	hword_t help_count = 0;
	waitcnt_t cnt = -1;
	lkword_t flag;
	lkword_t flagwait = COMBINE_FLG_WAIT;
	locktoken_t token;
	next_node = *pnode;
	/* cmb(); */
	cur_node = swp_rel(&s->tail, next_node);
	atm_st(&cur_node->next, next_node);
	flag = atm_ld(&cur_node->flag);
	while (flag == COMBINE_FLG_WAIT)
	{
		cnt = waitpolicy->waitf(&cur_node->flag, &flagwait, cnt);
		flag = atm_ld(&cur_node->flag);
	}

	if (flag == COMBINE_FLG_COMPLETED)
	{
		mp_rmb(); /* 同一cache行且存在读依赖 */
		*pnode = cur_node;
		return apds_args_ret(&cur_node->args);
	}

	/* 竞争获取全局锁 */
	lkop->lock(glock, &token);

	tmp_node = cur_node;
	tmp_node_next = next_node;
	do
	{
		/* APDS_PREFETCH(tmp_node_next); */
		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);
		tmp_node = tmp_node_next;
		if (++help_count >= APDS_HELP_QUOTA) break;
		tmp_node_next = tmp_node_next->next;
	}while (tmp_node_next);


	/* 释放全局锁 */
	lkop->unlock_relax(glock, token);
	/* 通知下一个 */
	atm_strel(&tmp_node->flag, COMBINE_FLG_OWNED);

	/* 唤醒线程 */
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&tmp_node->flag, &flag_owned);

	*pnode = cur_node;
	return apds_args_ret(&cur_node->args);
}
#define DEF_GHSYNC_STATE(name, numa_nr, lockname)		\
typedef struct _##name##_state {					\
	byte_t reserved_for_ghsyn[HDEF_CACHE_LINE_BYTES * numa_nr];	\
	lockname##lock_t ghsyn_glk;					\
} CACHE_ALIGNED _##name##_state_t;			/* 全局状态结构体，用于APDS_NUMA_NR个ghsyn*/\
static _##name##_state_t name;	/* 全局状态 */			\
SAI _ghsyn_t *name##_get_ghsyn(cid_t numa_id){ return (_ghsyn_t *)(&name.reserved_for_ghsyn[numa_id * HDEF_CACHE_LINE_BYTES]); }\
NEW_OUFUN(ghsyn_get, name, name##_get_ghsyn)

#define DEF_GHSYNC(name, numa_nr, cid2numa, lockname, pglock_instance, oufun_ghsyn_get)	\
typedef _ghsynnode_t _##name##node_t;						\
typedef _ghsyn_t _##name##_t;							\
PARAM_NONULL(1) SAI _##name##_t *_##name##_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))\
{\
	static bool_t glb_initialized = 0;\
	if (size < sizeof(_##name##_t)) return NULL;\
	_##name##_t *s = (_##name##_t *)ptr;\
	/* 全局初始化 */\
	if (!glb_initialized) { \
		/* 初始化全局锁 */\
		(OUFUN_OF(lockname))->init(pglock_instance, sizeof(lockname##lock_t));\
		for (cid_t idx = 0; idx < numa_nr; idx++){\
			_##name##node_t *node = (_##name##node_t *)nodemgr->get_lknode(sizeof(_##name##node_t));\
			__ghsyn_init((oufun_ghsyn_get)->get_ghsyn(idx), node);\
		}\
		atm_st(&glb_initialized, 1);\
	}\
	return s;\
}\
PARAM_NONULL(1) SAI void *_##name##_destruct(_##name##_t *s, INFUN(cmbn_node, nodemgr)){\
	_##name##node_t *node;\
	PARAM_USED(nodemgr);\
	void *rptr = __ghsyn_destruct(s, &node);\
	return rptr;\
}\
PARAM_NONULL(1, 2, 3) SAI _##name##node_t *_##name##_fetch (_##name##_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){\
	PARAM_USED(tinfo);\
	_##name##node_t *node = (_##name##node_t *)nodemgr->get_lknode(sizeof(_##name##node_t));\
	return __ghsyn_fetch(s, pfunc, args, node);\
}\
PARAM_NONULL(1, 2, 3) SAI apdsret_t _##name##_wait(_##name##_t *s, _##name##node_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){\
	PARAM_USED(s);\
	cid_t numa_id = cid2numa(tinfo->cid);\
	_##name##_t *p = (oufun_ghsyn_get)->get_ghsyn(numa_id);\
	apdsret_t ret = __ghsyn_wait(p, &node, pglock_instance, OUFUN_OF(lockname), waitpolicy);\
	nodemgr->ret_lknode(node, sizeof(_##name##node_t));\
	return ret;\
}



